package co.edu.javeriana.aes.patrones.monopolio.server;

import co.edu.javeriana.aes.patrones.monopolio.gameroom.Player;
import co.edu.javeriana.aes.patrones.monopolio.gameroom.GameRoom;
import co.edu.javeriana.aes.patrones.monopolio.gameroom.PlayerDTO;
import co.edu.javeriana.aes.patrones.monopolio.message.Message;
import co.edu.javeriana.aes.patrones.monopolio.message.MessageType;
import co.edu.javeriana.aes.patrones.monopolio.net.NetworkConnection;
import co.edu.javeriana.aes.patrones.monopolio.session.Session;
import co.edu.javeriana.aes.patrones.monopolio.util.exception.MonopolyException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MonopolyServer extends Thread {

    private ServerSocket serverSocket;
    private Set<ServerConnection> connections;
    private Map<Session, ServerConnection> sessionConnectionMap;
    private Set<GameRoom> rooms;
    private boolean stopMonopolyServer = false;

    public MonopolyServer() throws MonopolyException {
        try {
            serverSocket = new ServerSocket(9000);
        } catch (IOException ex) {
            throw new MonopolyException("Error while creating server socket.", ex);
        }
        connections = new HashSet<ServerConnection>();
        sessionConnectionMap = new HashMap<Session, ServerConnection>();
        rooms = new HashSet<GameRoom>();
    }

    @Override
    public void run() {
        while (!stopMonopolyServer) {
            System.out.println("Waiting for connection...");
            try {
                Socket newClient = serverSocket.accept();
                System.out.println("New Client arrived: " + newClient.getRemoteSocketAddress().toString());
                ServerConnection connection = new ServerConnection(newClient);
                connections.add(connection);
            } catch (MonopolyException ex) {
                Logger.getLogger(MonopolyServer.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(MonopolyServer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private void handleChatMessage(ServerConnection source, Message message) {
        for (ServerConnection connection : connections) {
            if (!source.equals(connection)) {
                connection.sendMessage(message);
            }
        }
//        GameRoom gameRoom = source.getGameRoom();
//        if (gameRoom != null) {
//            Iterator<PlayerDTO> playersInRoom = gameRoom.getPlayerDTOs().iterator();
//            while (playersInRoom.hasNext()) {
//                PlayerDTO currentPlayer = playersInRoom.next();
//                PlayerDTO playerDTO = new PlayerDTO(source.getGameRoom(), source.getSession());
//                if (!currentPlayer.equals(playerDTO)) {
//                    ServerConnection serverConnection = sessionConnectionMap.get(playerDTO.getSession());
//                    serverConnection.sendMessage(message);
//                }
//            }
//        }
    }

    private void handleRooms(ServerConnection source, Message message) {
        switch (message.getCommand()) {
            case R_CREATE_ROOM:
                GameRoom gameRoom = (GameRoom) message.getData();
                rooms.add(gameRoom);
                gameRoom.getPlayerDTOs().add(new PlayerDTO(source.getGameRoom(), source.getSession()));

                Iterator<PlayerDTO> players = gameRoom.getPlayerDTOs().iterator();
                while (players.hasNext()) {
                    Message refreshRoomPlayers = new Message(players.next().getSession(), source.getSession(), MessageType.R_PLAYER_JOINED);
                }
                break;

            case R_GET_ROOMS:
                ArrayList<String> roomNames = new ArrayList<String>();
                for (GameRoom room : rooms) {
                    roomNames.add(room.getName());
                }

                Message messageToSend = new Message(source.getSession(), roomNames, MessageType.R_LIST_OF_ROOMS);
                source.sendMessage(messageToSend);
                break;

            case R_JOIN_ROOM:
                String roomName = message.getData().toString();
                GameRoom room = null;

                for (GameRoom gr : rooms) {
                    if (gr.getName().equals(roomName)) {
                        room = gr;
                        break;
                    }
                }

                room.getPlayerDTOs().add(new PlayerDTO(source.getGameRoom(), source.getSession()));

                Message responseMessage = new Message(source.getSession(), room, MessageType.R_PLAYER_JOINED);
                source.sendMessage(responseMessage);
                break;

            case R_LIST_OF_ROOMS:
                break;
        }
    }

    private void handleSessionRequest(ServerConnection source, Message message) {
        Session newSession = new Session();
        newSession.setNickmame(message.getData().toString());
        newSession.setSessionId(new SecureRandom().nextLong());
        newSession.setHost(source.getRemoteHost());
        /**********************************************************************/
        Message sessionRequestResponse = new Message(newSession, message.getData(), MessageType.F_SESSION_RESPONSE);
        source.sendMessage(sessionRequestResponse);
        /**********************************************************************/
        sessionConnectionMap.put(newSession, source);
    }

    private void handlePlayerRequest(ServerConnection source, Message message) {
        switch (message.getCommand()) {
            case R_GET_LIST_OF_PLAYERS:

//                GameRoom gameRoom = null;
//                for (GameRoom gr : rooms) {
//                    if (gr.isPlayerInRoom(new PlayerDTO(source.getGameRoom(), source.getSession()))) {
//                        gameRoom = gr;
//                        break;
//                    }
//                }
//
//                ArrayList<String> playersNames = new ArrayList<String>();
//                Iterator<PlayerDTO> iterator = gameRoom.getPlayerDTOs().iterator();
//                while (iterator.hasNext()) {
//                    playersNames.add(iterator.next().getSession().getNickmame());
//                }
//
//                Message responseMessage = new Message(source.getSession(), playersNames, MessageType.R_LIST_OF_PLAYERS_RESPONSE);
//                source.sendMessage(responseMessage);
//                break;

            case R_LIST_OF_PLAYERS_RESPONSE:
                break;
        }
    }

    //<editor-fold defaultstate="collapsed" desc="stopMonopolyServer">
    public void stopMonopolyServer() {
        stopMonopolyServer = true;
        for (ServerConnection sc : connections) {
            sc.close();
        }
    }
    //</editor-fold>

    private class ServerConnection extends NetworkConnection implements Player {

        private GameRoom gameRoom;
        private Session session;

        public ServerConnection(Socket socket) throws MonopolyException {
            super(socket);
        }

        @Override
        public void handleMessage(Message message) {
            System.out.println(message);

            switch (message.getCommand()) {
                case R_CREATE_ROOM:

                case R_GET_ROOMS:
                case R_JOIN_ROOM:
                case R_LIST_OF_ROOMS:
                    handleRooms(this, message);
                case C_CHAT_MESSAGE:
                    handleChatMessage(this, message);
                    break;
                case F_SESSION_REQUEST:
                    handleSessionRequest(this, message);
                    break;
                case R_GET_LIST_OF_PLAYERS:
                    handlePlayerRequest(this, message);
                    break;
                case R_LIST_OF_PLAYERS_RESPONSE:
                    break;
            }
        }

        public void setGameRoom(GameRoom room) {
            this.gameRoom = room;
        }

        @Override
        public GameRoom getGameRoom() {
            return gameRoom;
        }

        public Session getSession() {
            return session;
        }

        public void setSession(Session session) {
            this.session = session;
        }
    }
}
